/*
 * F-List Pidgin - a libpurple protocol plugin for F-Chat
 *
 * Copyright 2011 F-List Pidgin developers.
 *
 * This file is part of F-List Pidgin.
 *
 * F-List Pidgin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * F-List Pidgin is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with F-List Pidgin.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "f-list_kinks.h"

typedef struct FListKink_ FListKink;

struct FListKinks_ {
    FListWebRequestData *global_kinks_request;
    GHashTable *kinks_table;
    GList *kinks_list;
    GSList *filter_channel_choices;
    GSList *filter_kink_choices;
};

struct FListKink_ {
    gchar *kink_id;
    gchar *name;
    gchar *category;
    gchar *description;
};

static inline FListKinks* _flist_kinks(FListAccount *fla) {
    return fla->flist_kinks;
}

static int flist_kinkcmp(FListKink *kink1, FListKink *kink2) {
    return strcmp(kink1->name, kink2->name);
}

static void flist_filter_action_cancel_cb(gpointer user_data, PurpleRequestFields *fields) {
    PurpleConnection *pc = user_data;
    FListAccount *fla = pc->proto_data;
    FListKinks *flk = _flist_kinks(fla);

    g_return_if_fail(fla);

    fla->input_request = FALSE;

    flist_g_slist_free_full(flk->filter_channel_choices, (GDestroyNotify) g_free);
    g_slist_free(flk->filter_kink_choices);
    flk->filter_channel_choices = NULL;
    flk->filter_kink_choices = NULL;
}

gboolean flist_process_FKS(PurpleConnection *pc, JsonObject *root) {
    const gchar *kink;
    JsonArray *characters;
    int i, len;
    GSList *character_list = NULL;
    
    kink = json_object_get_string_member(root, "kinkid");
    characters = json_object_get_array_member(root, "characters");

    len = json_array_get_length(characters);
    for(i = 0; i < len; i++) {
        const gchar *identity = json_array_get_string_element(characters, i);
        character_list = g_slist_prepend(character_list, (gpointer) identity); //we could copy this, but it doesn't matter
    }

    purple_debug_info(FLIST_DEBUG, "There are %d results to our kink search.\n", g_slist_length(character_list));
    
    flist_apply_filter(pc, TRUE, character_list);

    g_slist_free(character_list);

    return TRUE;
}

static void flist_filter_action_cb(gpointer user_data, PurpleRequestFields *fields) {
    PurpleConnection *pc = user_data;
    FListAccount *fla = pc->proto_data;
    FListKinks *flk = _flist_kinks(fla);
    JsonObject *json;
    JsonArray *genders, *kinks;
    int kink_choice, channel_choice;
    gchar *kink, *channel;

    g_return_if_fail(fla);

    kink_choice = purple_request_fields_get_choice(fields, "kink");
    channel_choice = purple_request_fields_get_choice(fields, "channel");

    kink = g_slist_nth_data(flk->filter_kink_choices, kink_choice);
    channel = g_slist_nth_data(flk->filter_channel_choices, channel_choice);

    if(fla->filter_channel) g_free(fla->filter_channel);
    if(channel) {
        fla->filter_channel = g_strdup(channel);
    } else {
        fla->filter_channel = NULL;
    }

    fla->filter_looking = purple_request_fields_get_bool(fields, "looking");

    purple_debug_info("flist", "We are processing a search request. Channel: %s; Kink: %s\n", channel, kink);

    if(kink) {
        GSList *cur;;
        json = json_object_new();
        genders = json_array_new();
        kinks = json_array_new();
        
        json_array_add_string_element(kinks, kink);
        
        cur = flist_get_gender_list();
        while(cur) {
            json_array_add_string_element(genders, cur->data);
            cur = g_slist_next(cur);
        }
        
        json_object_set_array_member(json, "kinks", kinks);
        json_object_set_array_member(json, "genders", genders);
        
        flist_request(pc, FLIST_KINK_SEARCH, json);
        
        /* unreference the json */
        json_array_unref(kinks);
        json_array_unref(genders);
        json_object_unref(json);
    } else {
        flist_apply_filter(pc, FALSE, NULL);
    }
    
    fla->input_request = FALSE;

    flist_g_slist_free_full(flk->filter_channel_choices, (GDestroyNotify) g_free);
    g_slist_free(flk->filter_kink_choices);
    flk->filter_channel_choices = NULL;
    flk->filter_kink_choices = NULL;
}

static void flist_filter_real(PurpleConnection *pc) {
    PurpleAccount *pa = purple_connection_get_account(pc);
    FListAccount *fla;
    FListKinks *flk;
    PurpleRequestFields *fields;
    PurpleRequestFieldGroup *group;
    PurpleRequestField *field;
    GList *channel_names, *cur;

    g_return_if_fail(pc);
    g_return_if_fail((fla = pc->proto_data));
    if(fla->input_request) return;
    flk = _flist_kinks(fla);

    group = purple_request_field_group_new("Options");
    fields = purple_request_fields_new();
    purple_request_fields_add_group(fields, group);

    /* TODO: remember choices from last time */

    field = purple_request_field_bool_new("looking", "Looking Only", TRUE);
    purple_request_field_group_add_field(group, field);

    /* now, add all of our current channels to the list */
    field = purple_request_field_choice_new("channel", _("Channel"), 0);
    purple_request_field_choice_add(field, "(No Filter)");
    flk->filter_channel_choices = g_slist_prepend(flk->filter_channel_choices, NULL);
    channel_names = flist_channel_list_names(fla);
    for(cur = channel_names; cur; cur = g_list_next(cur)) {
        gchar *channel = cur->data;
        purple_request_field_choice_add(field, channel);
        flk->filter_channel_choices = g_slist_prepend(flk->filter_channel_choices, g_strdup(channel));
    }
    g_list_free(channel_names);
    purple_request_field_group_add_field(group, field);
    flk->filter_channel_choices = g_slist_reverse(flk->filter_channel_choices);

    /* now, let's fill in the kink list part */
    if(flk->kinks_table) {
        field = purple_request_field_choice_new("kink", _("Kink"), 0);
        purple_request_field_choice_add(field, "(No Filter)");
        cur = flk->kinks_list; //TODO: filter down to our kinks only!
        flk->filter_kink_choices = g_slist_prepend(flk->filter_kink_choices, NULL);
        while(cur) {
            FListKink *kink = cur->data;
            purple_request_field_choice_add(field, kink->name);
            flk->filter_kink_choices = g_slist_prepend(flk->filter_kink_choices, kink->kink_id); //TODO: is this safe?
            cur = g_list_next(cur);
        }
        purple_request_field_group_add_field(group, field);
        flk->filter_kink_choices = g_slist_reverse(flk->filter_kink_choices);
    }

    /* TODO: find a not-awful way to filter genders */
    //list field didn't work (maybe I did something wrong?)
    //booleans take up too much space and make the window scroll and look terrible

    purple_request_fields(pc, _("Character Search!"), _("Search for characters on the F-List server."), _("Please fill out the search form."),
        fields,
        _("OK"), G_CALLBACK(flist_filter_action_cb),
        _("Cancel"), G_CALLBACK(flist_filter_action_cancel_cb),
        pa, NULL, NULL, pc);
}

void flist_filter_action(PurplePluginAction *action) {
    PurpleConnection *pc = action->context;
    flist_filter_real(pc);
}

PurpleCmdRet flist_filter_cmd(PurpleConversation *convo, const gchar *cmd, gchar **args, gchar **error, void *data) {
    PurpleConnection *pc = purple_conversation_get_gc(convo);

    flist_filter_real(pc);

    return PURPLE_CMD_STATUS_OK;
}

static void flist_global_kinks_cb(FListWebRequestData *req_data,
        gpointer user_data, JsonObject *root, const gchar *error_message) {
    FListAccount *fla = user_data;
    FListKinks *flk = _flist_kinks(fla);
    JsonObject *kinks;
    JsonArray *kinks_array;
    GList *categories, *cur;
    int i, len;
    
    flk->global_kinks_request = NULL;

    if(!root) {
        purple_debug_warning(FLIST_DEBUG, "Failed to obtain the global list of kinks. Error Message: %s\n", error_message);
        return;
    }
    
    kinks = json_object_get_object_member(root, "kinks");
    if(!kinks) {
        purple_debug_warning(FLIST_DEBUG, "We received the global list of kinks, but it was empty.\n");
        return;
    }

    flk->kinks_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); //TODO: write freeing function!

    categories = json_object_get_members(kinks);
    cur = categories;
    while(cur) {
        const gchar *category = cur->data;
        kinks_array = json_object_get_array_member(kinks, category);
        len = json_array_get_length(kinks_array);
        for(i = 0; i < len; i++) {
            JsonObject *kink_object = json_array_get_object_element(kinks_array, i);
            FListKink *kink = g_new0(FListKink, 1);
            kink->category = purple_unescape_html(category);
            kink->description = purple_unescape_html(json_object_get_string_member(kink_object, "description"));
            kink->name = purple_unescape_html(json_object_get_string_member(kink_object, "name"));
            kink->kink_id = purple_unescape_html(json_object_get_string_member(kink_object, "fetish_id"));
            g_hash_table_insert(flk->kinks_table, g_strdup(kink->name), kink);
        }
        cur = g_list_next(cur);
    }
    g_list_free(categories);

    purple_debug_info(FLIST_DEBUG, "We recieved the global list of kinks. Total kinks: %d\n", g_hash_table_size(flk->kinks_table));

    flk->kinks_list = g_list_sort(g_hash_table_get_values(flk->kinks_table), (GCompareFunc) flist_kinkcmp);
}

void flist_global_kinks_load(PurpleConnection *pc) {
    FListAccount *fla = pc->proto_data;
    FListKinks *flk;
    fla->flist_kinks = g_new0(FListKinks, 1);
    flk = _flist_kinks(fla);
    flk->global_kinks_request = flist_web_request(FLIST_GLOBAL_KINKS_URL, NULL, flist_global_kinks_cb, fla);
}

void flist_global_kinks_unload(PurpleConnection *pc) {
    FListAccount *fla = pc->proto_data;
    FListKinks *flk = _flist_kinks(fla);
    
    if(flk->global_kinks_request) {
        flist_web_request_cancel(flk->global_kinks_request);
        flk->global_kinks_request = NULL;
    }
    
    if(flk->kinks_list) {
        g_list_free(flk->kinks_list);
        flk->kinks_list = NULL;
    }
    if(flk->kinks_table) {
        g_hash_table_destroy(flk->kinks_table);
    }

    if(flk->filter_channel_choices) {
        flist_g_slist_free_full(flk->filter_channel_choices, (GDestroyNotify) g_free);
    }
    if(flk->filter_kink_choices) {
        g_slist_free(flk->filter_kink_choices);
    }
    
    g_free(flk);
    fla->flist_kinks = NULL;
}